Load libraries
library(here)
library(readr)
library(tidyr)
library(dplyr)
library(lubridate)
library(ggplot2)
library(stringr)
library(purrr)
library(readxl)
Read the data
db_EVA<-read_tsv(here("data", "edited", "db_EVA.csv"))
Aviso: One or more parsing issues, call `problems()` on your data frame for details,
e.g.:
dat <- vroom(...)
problems(dat)Rows: 1961807 Columns: 69── Column specification ──────────────────────────────────────────────────────────
Delimiter: "\t"
chr (30): Country, Biblioreference, Nr. table in publ., Nr. relevé in table, C...
dbl (15): PlotObservationID, PlotID, TV2 relevé number, Relevé area (m²), Alti...
lgl (24): RS_DUPL, RS_TIME, Lon_prec, Lat_prec, precision_new, Private, Lon1, ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Problems (do not affect us)
problems<-problems(db_EVA)
sort(unique(problems$col))
[1] 13 42
names(db_EVA[c(13, 42)])
[1] "Altitude" "RS_TIME"
Update coordinates
Create new column with old coordinates if new not available, and with
new if available.
db_EVA <- db_EVA %>%
mutate(Lon_updated = ifelse(is.na(Lon_prec),Longitude,Lon_prec),
Lat_updated = ifelse(is.na(Lat_prec),Latitude,Lat_prec))
print(db_EVA, width = Inf)
ISSUE 2: Coordinates are NA
Coordinates are also NA in some cases.
nrow(db_EVA%>%filter(is.na(Lon_updated) & is.na(Lat_updated )))
[1] 184081
Number of observations
Number of observations for each plot should be 1, as this is EVA
(without ReSurvey). I am not sure how to uniquely identify each plot
here!
Some dates recorded as 0:00:00: set to NA
ISSUE 5: Datasets with only presence/absence
db_EVA %>%
filter(`Cover abundance scale`=="Presence/Absence") %>%
distinct(Dataset)
ggplot(db_EVA %>%
mutate(pres_or_ab =ifelse(`Cover abundance scale`=="Presence/Absence",
"Presence/Absence", "Abundance"),
DK_Naturdata_Res = ifelse(Dataset == "DK_Naturdata_Res",
"Y", "N")),
aes(pres_or_ab, fill = DK_Naturdata_Res)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage", x = NULL)

# Very small % of DK_Naturdata_Res == "Y" in Presence/Absence
# but not visible in the figure
For DK_Naturdata_Res - info about habitat from Jerker’s file (see
below).
ISSUE 6: Observations with wrong country (TBD in GIS)
ISSUE 7: Different cover abundance scales
ggplot(db_EVA, aes(`Cover abundance scale`)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (without ReSurvey) observations",
x = "Cover abundance scale") +
coord_flip()

Supposing all abundance scales are equivalent.
EUNIS codes
Used this info in metadata file:
Expert system classification to EUNIS habitats (https://zenodo.org/records/4812736 ; https://floraveg.eu/habitat/). I am sending you legend
for EUNIS classification version 2022-10-16 with all codes and meanings,
directly prepared from expert system file (second sheet) - it is
slightly different from published version in ZENODO (https://zenodo.org/records/4812736 , little bit old
dated now) and from https://floraveg.eu/habitat/ (little bit newer than in
current EVA version).
Qa = mires and Qb = wetlands P units – in floraveg.eu there is
slightly different classification (https://floraveg.eu/habitat/overview/P), but in EVA is
still this classification of P:
P Surface waters Pa Base-poor spring and spring brook Pb Calcareous
spring and spring brook Pc Brackish-water vegetation Pd Fresh-water
small pleustophyte vegetation Pe Fresh-water large pleustophyte
vegetation Pf Fresh-water submerged vegetation Pg Fresh-water nymphaeid
vegetation Ph Oligotrophic-water vegetation Pi Dystrophic-water
vegetation Pj Stonewort vegetation
Presence of “!” simply means that for one unit there are two or more
different formulas, e.g. R11 and R11!. So it is only technical
stuff.
Multiple assignment of relevé – no priority, alphabetical order,
e.g. N16!,S66,S81 means that relevé can be assigned to all 3 units: N16
Mediterranean and Macaronesian coastal dune grassland (grey dune), S66
Mediterranean halo-nitrophilous scrub and S81 Canarian xerophytic
scrub
No value present in Expert System – relevé didn´t enter expert system
classification (= it means that some prerequisites are missing)
“~” – relevé entered expert classification however was not classified
to any EUNIS unit +
Clean info on Expert system column and separate it when there are
several codes.
db_EVA <- db_EVA %>%
mutate(
# Clean 'Expert System' column by removing "!" and replacing "~" with NA
`Expert System` = case_when(
`Expert System` == "~" ~ NA_character_, # Replace "~" with NA
TRUE ~ str_replace_all(`Expert System`, "!", "") # Remove "!"
)
) %>%
# Separate the values in 'Expert System' into multiple columns
separate(
`Expert System`,
into = c("EUNISa", "EUNISb", "EUNISc", "EUNISd"),
sep = ",",
extra = "drop", # Drop extra values if there are more than columns
fill = "right", # Fill missing values with NA for cases with fewer values
remove = FALSE # Keep the original 'Expert System' column
)
Calculate how many different EUNIS codes have been assigned:
db_EVA <- db_EVA %>%
mutate(
# Count the number of non-NA values across the EUNIS columns
n_EUNIS = rowSums(!is.na(select(., starts_with("EUNIS"))))
)
ggplot(db_EVA, aes(n_EUNIS)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (without ReSurvey) observations",
x = "Number of differnt EUNIS codes assigned") + coord_flip()

ggplot(db_EVA %>% filter(n_EUNIS > 0), aes(n_EUNIS)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (without ReSurvey) observations",
x = "Number of differnt EUNIS codes assigned") + coord_flip()

Correct some EUNIS codes that are probably wrong (copied from
ReSurvey notebook):
db_EVA <- db_EVA %>%
mutate(across(starts_with("EUNIS"), ~ case_when(
. == "N16M" ~ "N16",
. == "Sa" ~ "V4",
. == "Sb" ~ "V5",
. == "T1CT" ~ "T1C",
. == "N15A" ~ "N15",
TRUE ~ .
)))
Add columns for the different EUNIS levels
db_EVA <- db_EVA %>%
mutate(
# EUNISa levels
EUNISa_1 = substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 2, 1)),
EUNISa_2 = ifelse(
nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 3, 2),
substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 3, 2)),
NA_character_
),
EUNISa_3 = ifelse(
nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 4, 3),
substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 4, 3)),
NA_character_
),
EUNISa_4 = ifelse(
nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 5, 4),
substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 5, 4)),
NA_character_
),
# EUNISb levels
EUNISb_1 = substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 2, 1)),
EUNISb_2 = ifelse(
nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 3, 2),
substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 3, 2)),
NA_character_
),
EUNISb_3 = ifelse(
nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 4, 3),
substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 4, 3)),
NA_character_
),
EUNISb_4 = ifelse(
nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 5, 4),
substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 5, 4)),
NA_character_
),
# EUNISc levels
EUNISc_1 = substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 2, 1)),
EUNISc_2 = ifelse(
nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 3, 2),
substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 3, 2)),
NA_character_
),
EUNISc_3 = ifelse(
nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 4, 3),
substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 4, 3)),
NA_character_
),
EUNISc_4 = ifelse(
nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 5, 4),
substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 5, 4)),
NA_character_
),
# EUNISd levels
EUNISd_1 = substr(EUNISd, 1, ifelse(str_starts(EUNISc, "MA"), 2, 1)),
EUNISd_2 = ifelse(
nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 3, 2),
substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 3, 2)),
NA_character_
),
EUNISd_3 = ifelse(
nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 4, 3),
substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 4, 3)),
NA_character_
),
EUNISd_4 = ifelse(
nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 5, 4),
substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 5, 4)),
NA_character_
)
)
Add descriptions for level 1
db_EVA <- db_EVA %>%
mutate(
EUNISa_1_descr = case_when(
EUNISa_1 == "V" ~ "Vegetated man-made habitats",
EUNISa_1 == "U" ~ "Inland habitats with no or little soil",
EUNISa_1 == "T" ~ "Forests and other wooded land",
EUNISa_1 == "S" ~ "Heathlands, scrub and tundra",
EUNISa_1 == "R" ~ "Grasslands",
EUNISa_1 == "Q" ~ "Wetlands",
EUNISa_1 == "P" ~ "Inland waters",
EUNISa_1 == "N" ~ "Coastal habitats",
EUNISa_1 == "MA" ~ "Marine habitats",
TRUE ~ NA_character_
),
EUNISb_1_descr = case_when(
EUNISb_1 == "V" ~ "Vegetated man-made habitats",
EUNISb_1 == "U" ~ "Inland habitats with no or little soil",
EUNISb_1 == "T" ~ "Forests and other wooded land",
EUNISb_1 == "S" ~ "Heathlands, scrub and tundra",
EUNISb_1 == "R" ~ "Grasslands",
EUNISb_1 == "Q" ~ "Wetlands",
EUNISb_1 == "P" ~ "Inland waters",
EUNISb_1 == "N" ~ "Coastal habitats",
EUNISb_1 == "MA" ~ "Marine habitats",
TRUE ~ NA_character_
),
EUNISc_1_descr = case_when(
EUNISc_1 == "V" ~ "Vegetated man-made habitats",
EUNISc_1 == "U" ~ "Inland habitats with no or little soil",
EUNISc_1 == "T" ~ "Forests and other wooded land",
EUNISc_1 == "S" ~ "Heathlands, scrub and tundra",
EUNISc_1 == "R" ~ "Grasslands",
EUNISc_1 == "Q" ~ "Wetlands",
EUNISc_1 == "P" ~ "Inland waters",
EUNISc_1 == "N" ~ "Coastal habitats",
EUNISc_1 == "MA" ~ "Marine habitats",
TRUE ~ NA_character_
),
EUNISd_1_descr = case_when(
EUNISd_1 == "V" ~ "Vegetated man-made habitats",
EUNISd_1 == "U" ~ "Inland habitats with no or little soil",
EUNISd_1 == "T" ~ "Forests and other wooded land",
EUNISd_1 == "S" ~ "Heathlands, scrub and tundra",
EUNISd_1 == "R" ~ "Grasslands",
EUNISd_1 == "Q" ~ "Wetlands",
EUNISd_1 == "P" ~ "Inland waters",
EUNISd_1 == "N" ~ "Coastal habitats",
EUNISd_1 == "MA" ~ "Marine habitats",
TRUE ~ NA_character_
)
)
Plot for EUNISa_1 (the first assigned EUNIS in cases of multiple
assignations, level 1):
ggplot(db_EVA, aes(EUNISa_1_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (without ReSurvey) observations",
x = "EUNIS level 1") + coord_flip()

ggplot(db_EVA %>% filter(!is.na(EUNISa_1_descr)), aes(EUNISa_1_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (without ReSurvey) observations",
x = "EUNIS level 1") + coord_flip()

Check if values of EUNISa_1, EUNISb_1, EUNISc_1 and EUNISd_1 are
among the allowed values:
db_EVA %>%
select(all_of(c("EUNISa_1", "EUNISb_1", "EUNISc_1", "EUNISd_1"))) %>%
pivot_longer(everything(), names_to = "column", values_to = "value") %>%
filter(!is.na(value)) %>%
distinct(column, value) %>%
mutate(is_valid = value %in% EUNIS1_codes) %>%
group_by(column) %>%
summarise(
all_valid = all(is_valid),
invalid_values = list(value[!is_valid]))
All level 1 values are valid.
CHECK IF THERE ARE WRONG EUNIS VALUES FROM LEVEL 2 AND UP
ISSUE 9: Manipulated plots and info on manipulation type
ggplot(db_EVA, aes(`Manipulate (y/n)`)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "Manipulation")

List of Type of Manipulation (mixed information), supposedly no
manipulated plots here (!?):
unique(db_EVA$`Type of manipulation`)
[1] NA "reference forest" "x"
ISSUE 10: Location method
ggplot(db_EVA, aes(`Location method`)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (without ReSurvey) observations",
x = "Location method") + coord_flip()

ISSUE 11: Resurvey project types
unique(db_EVA$RS_PROJTYP)
[1] NA "permanent" "resampling"
ggplot(db_EVA, aes(RS_PROJTYP, fill=`Manipulate (y/n)`)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (without ReSurvey) observations",
x = "Resurvey project type") + coord_flip() +
theme(legend.position = "top")

ISSUE 13: Location uncertainty
db_EVA <- db_EVA %>%
# Redefine precision_new, which was wrong
mutate(precision_new = factor(ifelse(is.na(Lon_prec) & is.na(Lat_prec),
0, 1)))
ggplot(db_EVA, aes(`Location uncertainty (m)`, fill = precision_new)) +
geom_histogram( color = "black") +
xlab("Location uncertainty (m)")

ggplot(db_EVA %>% filter(`Location uncertainty (m)` <= 500),
aes(`Location uncertainty (m)`, fill = precision_new)) +
geom_histogram(color = "black") +
xlab("Location uncertainty (m) <= 500")

ggplot(db_EVA %>% filter(`Location uncertainty (m)` > 500),
aes(`Location uncertainty (m)`, fill = precision_new)) +
geom_histogram(color = "black") +
xlab("Location uncertainty (m) > 500")

NO ISSUES FROM HERE
Altitude and slope values: REVISE IF USE!
Unique slope values:
unique((db_EVA)$`Slope (°)`) %>% str_sort()
[1] "-1" "-2" "-5" "-9" "0" "0.5" "1" "10" "11" "12" "13" "14"
[13] "15" "16" "17" "18" "19" "2" "20" "21" "22" "23" "24" "25"
[25] "26" "27" "28" "29" "3" "30" "31" "32" "33" "34" "35" "36"
[37] "37" "38" "39" "4" "40" "41" "42" "43" "44" "45" "46" "47"
[49] "48" "49" "5" "50" "51" "52" "53" "54" "55" "56" "57" "58"
[61] "59" "6" "60" "61" "62" "63" "64" "65" "66" "67" "68" "69"
[73] "7" "70" "71" "72" "73" "74" "75" "76" "77" "78" "79" "8"
[85] "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" "9" "90"
[97] "92" "93" "95" "97" "98" "99" NA
Set altitude, slope and aspect as numeric:
db_EVA <- db_EVA %>%
mutate(
# Some altitude values have a "-" after the number,
# convert to numeric after removing that
Altitude = as.numeric(gsub("-", "", Altitude)),
# Some slope values are noted as "_" or "-", these should be NA,
# otherwise convert to numeric
`Slope (°)` = ifelse(`Slope (°)` == "_" | `Slope (°)` == "-",
NA, as.numeric(`Slope (°)`)),
# Convert aspect values to numeric
`Aspect (°)` = as.numeric(`Aspect (°)`)
)
Aviso: There was 1 warning in `mutate()`.
ℹ In argument: `Slope (°) = ifelse(`Slope (°)` == "_" | `Slope (°)` == "-", NA,
as.numeric(`Slope (°)`))`.
Caused by warning in `ifelse()`:
! NAs introducidos por coerción
Histograms:
ggplot(db_EVA, aes(Altitude)) +
geom_histogram(fill = "white", color = "black")

ggplot(db_EVA, aes(`Aspect (°)`)) +
geom_histogram(fill = "white", color = "black")

ggplot(db_EVA, aes(`Slope (°)`)) +
geom_histogram(fill = "white", color = "black")

range(db_EVA$`Slope (°)`, na.rm=T)
[1] -9 99
Add columns date and year
db_EVA <- db_EVA %>%
mutate(date = dmy(`Date of recording`), year = year(date))
Aviso: There was 1 warning in `mutate()`.
ℹ In argument: `date = dmy(`Date of recording`)`.
Caused by warning:
! 98 failed to parse.
Histograms:
ggplot(db_EVA, aes(year)) + geom_histogram(fill = "white", color = "black")

Plot size
ggplot(db_EVA, aes(`Relevé area (m²)`)) +
geom_histogram(fill = "white", color = "black")

Observations with no info on plot size:
nrow(db_EVA %>% filter(is.na(`Relevé area (m²)`)))
[1] 465792
Cover values (total, trees, shrubs, herbs, mossess)
db_EVA %>%
pivot_longer(cols = c(`Cover total (%)`, `Cover tree layer (%)`,
`Cover shrub layer (%)`, `Cover herb layer (%)`,
`Cover moss layer (%)`),
names_to = "variable", values_to = "value") %>%
ggplot(aes(x = value)) +
geom_histogram(fill = "white", color = "black", bins = 10) +
facet_wrap(~ variable, scales = "free") +
labs(x = "Value", y = "Frequency")

db_EVA %>%
reframe(across(c(`Cover total (%)`, `Cover tree layer (%)`,
`Cover shrub layer (%)`, `Cover herb layer (%)`,
`Cover moss layer (%)`), ~range(., na.rm = TRUE)))
All values OK.
Mosses and lichens identified
ggplot(db_EVA, aes(`Mosses identified (Y/N)`)) + geom_bar()

ggplot(db_EVA, aes(`Lichens identified (Y/N)`)) + geom_bar()

NA in most cases.
Info on HabitatID from DK
Based on information got from Jesper.
Read the data sent by Jesper from DK
db_DK_J<-read_tsv(here("data", "raw",
"DK_Naturdata_Res_habitat_hab_codes_Jesper",
"DK_Naturdata_Res_habitat_hab_codes.txt"))
Rows: 158800 Columns: 9── Column specification ──────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): HABITAT, Dataset
dbl (7): PlotObservationID, RELEVE_NR, CIRC_ID, PLOT5_ID, PLOT15_ID, Access re...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Add info on HabitatID to db_EVA
db_EVA <- db_EVA %>%
# Keeping all obs in db_EVA but not all in db_DK_J
left_join(db_DK_J %>% select(PlotObservationID, HabitatID))
Joining with `by = join_by(PlotObservationID)`
List of HabitatID
print(db_EVA %>% distinct(HabitatID), n = 100)
Write csv:
write_csv(db_EVA %>% distinct(HabitatID),
here("data", "clean","list_HabitatID_DK_EVA.csv"))
Cases without HabitatID info
Cases without ESy EUNIS habitat:
nrow(db_EVA %>% filter(is.na(`Expert System`)))/nrow(db_EVA)
[1] 0.3751393
Cases without ESy EUNIS habitat but with HabitatID from DK:
nrow(db_EVA %>% filter(is.na(`Expert System`)&!is.na(HabitatID)))/nrow(db_EVA)
[1] 0.0001090831
Cases without ESy EUNIS habitat and without HABITAT from DK:
nrow(db_EVA %>%
filter(is.na(`Expert System`)&is.na(HabitatID)))/nrow(db_EVA)
[1] 0.3750303
Cases without ESy EUNIS habitat and without HabitatID from DK where
data is presence / absence:
nrow(db_EVA %>%
filter(is.na(`Expert System`) &
is.na(HabitatID) &
`Cover abundance scale` == "Presence/Absence"))/
nrow(db_EVA)
[1] 0.1504649
Cases without ESy EUNIS habitat and without HabitatID from DK where
data is not presence / absence:
nrow(db_EVA %>%
filter(is.na(`Expert System`) &
is.na(HabitatID) &
`Cover abundance scale` != "Presence/Absence"))/
nrow(db_EVA)
[1] 0.2245654
Change some Annex I habitat codes that were wrong
db_EVA <- db_EVA %>%
mutate(HabitatID = as.character(HabitatID)) %>%
mutate(HabitatID = ifelse(HabitatID == "9998", "91D0",
ifelse(HabitatID == "9999", "91E0", HabitatID)))
Add info on correspondences HabitatID (DK, Jesper) - EUNIS
Read correspondences file:
correspondences<-read_excel(here("data", "edited",
"correspondence_HabitatID_DK.xlsx"))
Add info to db_EVA:
db_EVA <- db_EVA %>%
# Keeping all obs in db_EVA but not all in db_DK_J
left_join(correspondences %>% select(HabitatID, EUNIS))
Joining with `by = join_by(HabitatID)`
Correct NA values in EUNIS
db_EVA <- db_EVA %>%
mutate(EUNIS = ifelse(EUNIS == "NA", NA, EUNIS))
Add info on EUNIS (DK) to EUNISa:
db_EVA <- db_EVA %>%
mutate(EUNISa =
# If EUNIS (DK) is available, add as EUNISa
ifelse(!is.na(EUNIS), EUNIS,
# Otherwise keep EUNISa
EUNISa),
EUNIS_assignation = ifelse(!is.na(EUNIS), "Info from DK",
ifelse(is.na(EUNISa), "Not possible",
"Expert system"))) %>%
# Remove column EUNIS (DK)
select(-EUNIS)
ggplot(db_EVA, aes(EUNIS_assignation)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (not ReSurvey) observations",
x = "EUNIS assignation")

Update columns for EUNIS levels and descriptions
Update the columns for the different EUNISs levels:
db_EVA <- db_EVA %>%
mutate(
# EUNISa levels
EUNISa_1 = substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 2, 1)),
EUNISa_2 = ifelse(
nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 3, 2),
substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 3, 2)),
NA_character_
),
EUNISa_3 = ifelse(
nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 4, 3),
substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 4, 3)),
NA_character_
),
EUNISa_4 = ifelse(
nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 5, 4),
substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 5, 4)),
NA_character_
)
) %>%
# Remove HabitatID column
select(-HabitatID)
Update columns with descriptions for the level 1 codes:
db_EVA <- db_EVA %>%
mutate(
EUNISa_1_descr = case_when(
EUNISa_1 == "V" ~ "Vegetated man-made habitats",
EUNISa_1 == "U" ~ "Inland habitats with no or little soil",
EUNISa_1 == "T" ~ "Forests and other wooded land",
EUNISa_1 == "S" ~ "Heathlands, scrub and tundra",
EUNISa_1 == "R" ~ "Grasslands",
EUNISa_1 == "Q" ~ "Wetlands",
EUNISa_1 == "P" ~ "Inland waters",
EUNISa_1 == "N" ~ "Coastal habitats",
EUNISa_1 == "MA" ~ "Marine habitats",
TRUE ~ NA_character_
)
)
Number of different EUNIS codes
Recalculate how many different EUNIS codes have been assigned:
db_EVA <- db_EVA %>%
mutate(
# Count the number of non-NA values across the EUNIS columns
n_EUNIS = rowSums(!is.na(select(., EUNISa:EUNISd)))
)
ggplot(db_EVA, aes(n_EUNIS)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (not ReSurvey) observations",
x = "Number of differnt EUNIS codes assigned") + coord_flip()

ggplot(db_EVA %>% filter(n_EUNIS > 0), aes(n_EUNIS)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (not ReSurvey) observations",
x = "Number of differnt EUNIS codes assigned") + coord_flip()

New plot for EUNISa_1 (the first assigned EUNIS in cases of multiple
assignations, level 1):
ggplot(db_EVA, aes(EUNISa_1_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (not ReSurvey) observations",
x = "EUNIS level 1") + coord_flip()

ggplot(db_EVA %>% filter(!is.na(EUNISa_1_descr)), aes(EUNISa_1_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of EVA (not ReSurvey) observations",
x = "EUNIS level 1") + coord_flip()

Add info on descriptions for EUNIS levels 2-4 - CHECK IF USE!
descriptions<-read_excel(here("data", "edited",
"EUNIS-Habitats-2021-06-01_modified.xlsx"))
# Define the columns and corresponding description column names
eunis_cols <- c("EUNISa_2", "EUNISa_3", "EUNISa_4",
"EUNISb_2", "EUNISb_3", "EUNISb_4",
"EUNISc_2", "EUNISc_3", "EUNISc_4",
"EUNISd_2", "EUNISd_3", "EUNISd_4")
# Create corresponding description column names
descr_col_names <- paste0(eunis_cols, "_descr")
# Use reduce to loop through the columns and join dynamically based on level
db_EVA <- reduce(seq_along(eunis_cols), function(data, i) {
# Extract level number from the column name (e.g., EUNISa_2 -> 2)
level <- as.numeric(gsub("\\D", "", eunis_cols[i]))
# Filter descriptions for the corresponding level
descriptions_level <- descriptions %>%
filter(level == level) %>%
select(`EUNIS 2020 code`, `EUNIS-2021 habitat name`)
# Perform the left_join and rename the column dynamically
data %>%
left_join(
descriptions_level,
by = setNames("EUNIS 2020 code", eunis_cols[i])
) %>%
rename(!!descr_col_names[i] := `EUNIS-2021 habitat name`)
}, .init = db_EVA)
The matching did not work sometimes, correct!
# Correct EUNISa levels 2-4 descriptions
db_EVA <- db_EVA %>%
mutate(EUNISa_2_descr =
ifelse(!is.na(EUNISa_2_descr), EUNISa_2_descr,
case_when(
EUNISa_2 == "Pf" ~ "Fresh-water submerged vegetation",
EUNISa_2 == "Pj" ~ "Stonewort vegetation",
EUNISa_2 == "R4" ~ "Alpine and subalpine grasslands",
EUNISa_2 == "Pb" ~ "Calcareous spring and spring brook",
EUNISa_2 == "Qb" ~ "Wetlands",
EUNISa_2 == "R3" ~ "Seasonally wet and wet grasslands",
EUNISa_2 == "Qa" ~ "Mires",
EUNISa_2 == "Pa" ~ "Base-poor spring and spring brook",
EUNISa_2 == "Ph" ~ "Oligotrophic-water vegetation",
EUNISa_2 == "Pg" ~ "Fresh-water nymphaeid vegetation",
EUNISa_2 ==
"Pd" ~ "Fresh-water small pleustophyte vegetation",
EUNISa_2 == "Pc" ~ "Brackish-water vegetation",
EUNISa_2 ==
"Pe" ~ "Fresh-water large pleustophyte vegetation",
EUNISa_2 == "Pi" ~ "Dystrophic-water vegetation",
EUNISa_2 == "S1" ~ "Tundra",
EUNISa_2 ==
"U7" ~ "Unvegetated or sparsely vegetated gravel bars",
EUNISa_2 == "Q6" ~ "Periodically exposed shores",
TRUE ~ NA_character_)
),
EUNISa_3_descr =
ifelse(!is.na(EUNISa_3_descr), EUNISa_3_descr,
case_when(
EUNISa_3 =="U71" ~ "Unvegetated or sparsely vegetated gravel bar in montane and alpine regions",
EUNISa_3 =="Q61" ~ "Periodically exposed shore with stable, eutrophic sediments with pioneer or ephemeral vegetation",
EUNISa_3 =="Q62" ~ "Periodically exposed shore with stable, mesotrophic sediments with pioneer or ephemeral vegetation",
TRUE ~ NA_character_
))
)
# Correct EUNISb levels 2-4 descriptions
db_EVA <- db_EVA %>%
mutate(EUNISb_2_descr =
ifelse(!is.na(EUNISb_2_descr), EUNISb_2_descr,
case_when(
EUNISb_2 == "Pj" ~ "Stonewort vegetation",
EUNISb_2 == "R4" ~ "Alpine and subalpine grasslands",
EUNISb_2 == "Pf" ~ "Fresh-water submerged vegetation",
TRUE ~ NA_character_)
)
)
EUNISc and EUNISd levels 2-4 are OK.
Notes EUNIS codes - to change?
https://www.sci.muni.cz/botany/chytry/Schaminee_etal2021_EEA-Report-Aquatic-Wetland-habitats.pdf
EUNISa_2 == “Q6” : “Periodically exposed shores” EUNISa_3 = “Q61” :
“Periodically exposed shore with stable, eutrophic sediments with
pioneer or ephemeral vegetation” EUNISa_3 == “Q62” : “Periodically
exposed shore with stable, mesotrophic sediments with pioneer or
ephemeral vegetation”
This classification of Q + numbers is now coexisting in the database
with Qa & Qb (metadata). How to proceed?
db_EVA %>% filter(EUNISa_1 == "Q") %>% distinct(EUNISa_2)
Plots of level-2 categories within each level 1 category
ggplot(db_EVA %>% filter(EUNISa_1 == "MA"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "MA") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","MA_level2.tiff"),
width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "N"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "N") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","N_level2.tiff"),
width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "P"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "P") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","P_level2.tiff"),
width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "Q"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "Q") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","Q_level2.tiff"),
width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "R"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "R") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","R_level2.tiff"),
width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "S"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "S") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","S_level2.tiff"),
width=16,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "T"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "T") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","T_level2.tiff"),
width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "U"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "U") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","U_level2.tiff"),
width=16,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "V"), aes(EUNISa_2_descr)) +
geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
labs(y = "Percentage of ReSurvey observations",
x = "EUNIS level 2") + coord_flip() +
ggtitle(db_EVA %>% filter(EUNISa_1 == "V") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","V_level2.tiff"),
width=14,height=8,units="cm",dpi=300)

Save to clean data
Save so-far clean datafile for EVA database (not resurveys):
write_tsv(db_EVA,here("data", "clean","db_EVA_clean.csv"))
Session info
sessionInfo()
LS0tDQp0aXRsZTogIlNjcmlwdCB0byBtYWtlIGEgZmlyc3QgY2hlY2sgb2YgdGhlIEVWQSBkYXRhYmFzZSAobm90IHJlc3VydmV5cykgZm9yIE1PVElWQVRFIg0Kb3V0cHV0Og0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KLS0tDQoNCiMgTG9hZCBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHJlYWR4bCkNCmBgYA0KDQojIFJlYWQgdGhlIGRhdGENCg0KYGBge3J9DQpkYl9FVkE8LXJlYWRfdHN2KGhlcmUoImRhdGEiLCAiZWRpdGVkIiwgImRiX0VWQS5jc3YiKSkNCmBgYA0KDQojIFByb2JsZW1zIChkbyBub3QgYWZmZWN0IHVzKQ0KDQpgYGB7cn0NCnByb2JsZW1zPC1wcm9ibGVtcyhkYl9FVkEpDQpzb3J0KHVuaXF1ZShwcm9ibGVtcyRjb2wpKQ0KbmFtZXMoZGJfRVZBW2MoMTMsIDQyKV0pDQpgYGANCg0KIyBVcGRhdGUgY29vcmRpbmF0ZXMNCg0KQ3JlYXRlIG5ldyBjb2x1bW4gd2l0aCBvbGQgY29vcmRpbmF0ZXMgaWYgbmV3IG5vdCBhdmFpbGFibGUsIGFuZCB3aXRoIG5ldyBpZiBhdmFpbGFibGUuDQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgIyBMb25fcHJlYyB5IExhdF9wcmVjIGFsd2F5cyBOQSENCiAgbXV0YXRlKExvbl91cGRhdGVkID0gaWZlbHNlKGlzLm5hKExvbl9wcmVjKSxMb25naXR1ZGUsTG9uX3ByZWMpLA0KICAgICAgICAgTGF0X3VwZGF0ZWQgPSBpZmVsc2UoaXMubmEoTGF0X3ByZWMpLExhdGl0dWRlLExhdF9wcmVjKSkNCmBgYA0KDQpgYGB7cn0NCnByaW50KGRiX0VWQSwgd2lkdGggPSBJbmYpDQpgYGANCg0KIyBJU1NVRSAyOiBDb29yZGluYXRlcyBhcmUgTkENCg0KQ29vcmRpbmF0ZXMgYXJlIGFsc28gTkEgaW4gc29tZSBjYXNlcy4NCg0KYGBge3J9DQpucm93KGRiX0VWQSU+JWZpbHRlcihpcy5uYShMb25fdXBkYXRlZCkgJiBpcy5uYShMYXRfdXBkYXRlZCApKSkNCmBgYA0KDQojIE51bWJlciBvZiBvYnNlcnZhdGlvbnMNCg0KTnVtYmVyIG9mIG9ic2VydmF0aW9ucyBmb3IgZWFjaCBwbG90IHNob3VsZCBiZSAxLCBhcyB0aGlzIGlzIEVWQSAod2l0aG91dCBSZVN1cnZleSkuDQpJIGFtIG5vdCBzdXJlIGhvdyB0byB1bmlxdWVseSBpZGVudGlmeSBlYWNoIHBsb3QgaGVyZSENCg0KIyBTb21lIGRhdGVzIHJlY29yZGVkIGFzIDA6MDA6MDA6IHNldCB0byBOQQ0KDQojIElTU1VFIDU6IERhdGFzZXRzIHdpdGggb25seSBwcmVzZW5jZS9hYnNlbmNlDQoNCmBgYHtyfQ0KZGJfRVZBICU+JQ0KICBmaWx0ZXIoYENvdmVyIGFidW5kYW5jZSBzY2FsZWA9PSJQcmVzZW5jZS9BYnNlbmNlIikgJT4lDQogIGRpc3RpbmN0KERhdGFzZXQpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGJfRVZBICU+JSANCiAgICAgICAgIG11dGF0ZShwcmVzX29yX2FiID1pZmVsc2UoYENvdmVyIGFidW5kYW5jZSBzY2FsZWA9PSJQcmVzZW5jZS9BYnNlbmNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlByZXNlbmNlL0Fic2VuY2UiLCAiQWJ1bmRhbmNlIiksDQogICAgICAgICAgICAgICAgREtfTmF0dXJkYXRhX1JlcyA9IGlmZWxzZShEYXRhc2V0ID09ICJES19OYXR1cmRhdGFfUmVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJZIiwgIk4iKSksDQogICAgICAgICAgICAgICAgYWVzKHByZXNfb3JfYWIsIGZpbGwgPSBES19OYXR1cmRhdGFfUmVzKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSIsIHggPSBOVUxMKQ0KIyBWZXJ5IHNtYWxsICUgb2YgREtfTmF0dXJkYXRhX1JlcyA9PSAiWSIgaW4gUHJlc2VuY2UvQWJzZW5jZSANCiMgYnV0IG5vdCB2aXNpYmxlIGluIHRoZSBmaWd1cmUNCmBgYA0KDQpGb3IgREtfTmF0dXJkYXRhX1JlcyAtIGluZm8gYWJvdXQgaGFiaXRhdCBmcm9tIEplcmtlcidzIGZpbGUgKHNlZSBiZWxvdykuDQoNCiMgSVNTVUUgNjogT2JzZXJ2YXRpb25zIHdpdGggd3JvbmcgY291bnRyeSAoVEJEIGluIEdJUykNCg0KIyBJU1NVRSA3OiBEaWZmZXJlbnQgY292ZXIgYWJ1bmRhbmNlIHNjYWxlcw0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgQ292ZXIgYWJ1bmRhbmNlIHNjYWxlYCkpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgRVZBICh3aXRob3V0IFJlU3VydmV5KSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiQ292ZXIgYWJ1bmRhbmNlIHNjYWxlIikgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQpTdXBwb3NpbmcgYWxsIGFidW5kYW5jZSBzY2FsZXMgYXJlIGVxdWl2YWxlbnQuDQoNCiMgRVVOSVMgY29kZXMNCg0KVXNlZCB0aGlzIGluZm8gaW4gbWV0YWRhdGEgZmlsZToNCg0KRXhwZXJ0IHN5c3RlbSBjbGFzc2lmaWNhdGlvbiB0byBFVU5JUyBoYWJpdGF0cyAoaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvNDgxMjczNiA7IGh0dHBzOi8vZmxvcmF2ZWcuZXUvaGFiaXRhdC8pLiANCkkgYW0gc2VuZGluZyB5b3UgbGVnZW5kIGZvciBFVU5JUyBjbGFzc2lmaWNhdGlvbiB2ZXJzaW9uIDIwMjItMTAtMTYgd2l0aCBhbGwgY29kZXMgYW5kIG1lYW5pbmdzLCBkaXJlY3RseSBwcmVwYXJlZCBmcm9tIGV4cGVydCBzeXN0ZW0gZmlsZSAoc2Vjb25kIHNoZWV0KSAtIGl0IGlzIHNsaWdodGx5IGRpZmZlcmVudCBmcm9tIHB1Ymxpc2hlZCB2ZXJzaW9uIGluIFpFTk9ETyAoaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvNDgxMjczNiAsIGxpdHRsZSBiaXQgb2xkIGRhdGVkIG5vdykgYW5kIGZyb20gaHR0cHM6Ly9mbG9yYXZlZy5ldS9oYWJpdGF0LyAobGl0dGxlIGJpdCBuZXdlciB0aGFuIGluIGN1cnJlbnQgRVZBIHZlcnNpb24pLg0KDQpRYSA9IG1pcmVzIGFuZCBRYiA9IHdldGxhbmRzDQpQIHVuaXRzIOKAkyBpbiBmbG9yYXZlZy5ldSB0aGVyZSBpcyBzbGlnaHRseSBkaWZmZXJlbnQgY2xhc3NpZmljYXRpb24gKGh0dHBzOi8vZmxvcmF2ZWcuZXUvaGFiaXRhdC9vdmVydmlldy9QKSwgYnV0IGluIEVWQSBpcyBzdGlsbCB0aGlzIGNsYXNzaWZpY2F0aW9uIG9mIFA6DQoNClAgU3VyZmFjZSB3YXRlcnMNClBhIEJhc2UtcG9vciBzcHJpbmcgYW5kIHNwcmluZyBicm9vaw0KUGIgQ2FsY2FyZW91cyBzcHJpbmcgYW5kIHNwcmluZyBicm9vaw0KUGMgQnJhY2tpc2gtd2F0ZXIgdmVnZXRhdGlvbg0KUGQgRnJlc2gtd2F0ZXIgc21hbGwgcGxldXN0b3BoeXRlIHZlZ2V0YXRpb24NClBlIEZyZXNoLXdhdGVyIGxhcmdlIHBsZXVzdG9waHl0ZSB2ZWdldGF0aW9uDQpQZiBGcmVzaC13YXRlciBzdWJtZXJnZWQgdmVnZXRhdGlvbg0KUGcgRnJlc2gtd2F0ZXIgbnltcGhhZWlkIHZlZ2V0YXRpb24NClBoIE9saWdvdHJvcGhpYy13YXRlciB2ZWdldGF0aW9uDQpQaSBEeXN0cm9waGljLXdhdGVyIHZlZ2V0YXRpb24NClBqIFN0b25ld29ydCB2ZWdldGF0aW9uIA0KDQpQcmVzZW5jZSBvZiDigJwh4oCdIHNpbXBseSBtZWFucyB0aGF0IGZvciBvbmUgdW5pdCB0aGVyZSBhcmUgdHdvIG9yIG1vcmUgZGlmZmVyZW50IGZvcm11bGFzLCBlLmcuIFIxMSBhbmQgUjExIS4gU28gaXQgaXMgb25seSB0ZWNobmljYWwgc3R1ZmYuDQoNCk11bHRpcGxlIGFzc2lnbm1lbnQgb2YgcmVsZXbDqSDigJMgbm8gcHJpb3JpdHksIGFscGhhYmV0aWNhbCBvcmRlciwgZS5nLiBOMTYhLFM2NixTODEgbWVhbnMgdGhhdCByZWxldsOpIGNhbiBiZSBhc3NpZ25lZCB0byBhbGwgMyB1bml0czogTjE2IE1lZGl0ZXJyYW5lYW4gYW5kIE1hY2Fyb25lc2lhbiBjb2FzdGFsIGR1bmUgZ3Jhc3NsYW5kIChncmV5IGR1bmUpLCBTNjYgTWVkaXRlcnJhbmVhbiBoYWxvLW5pdHJvcGhpbG91cyBzY3J1YiBhbmQgUzgxIENhbmFyaWFuIHhlcm9waHl0aWMgc2NydWINCg0KTm8gdmFsdWUgcHJlc2VudCBpbiBFeHBlcnQgU3lzdGVtIOKAkyByZWxldsOpIGRpZG7CtHQgZW50ZXIgZXhwZXJ0IHN5c3RlbSBjbGFzc2lmaWNhdGlvbiAoPSBpdCBtZWFucyB0aGF0IHNvbWUgcHJlcmVxdWlzaXRlcyBhcmUgbWlzc2luZykNCg0K4oCcfuKAnSDigJMgcmVsZXbDqSBlbnRlcmVkIGV4cGVydCBjbGFzc2lmaWNhdGlvbiBob3dldmVyIHdhcyBub3QgY2xhc3NpZmllZCB0byBhbnkgRVVOSVMgdW5pdA0KKw0KDQpDbGVhbiBpbmZvIG9uIEV4cGVydCBzeXN0ZW0gY29sdW1uIGFuZCBzZXBhcmF0ZSBpdCB3aGVuIHRoZXJlIGFyZSBzZXZlcmFsIGNvZGVzLg0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICAjIENsZWFuICdFeHBlcnQgU3lzdGVtJyBjb2x1bW4gYnkgcmVtb3ZpbmcgIiEiIGFuZCByZXBsYWNpbmcgIn4iIHdpdGggTkENCiAgICBgRXhwZXJ0IFN5c3RlbWAgPSBjYXNlX3doZW4oDQogICAgICBgRXhwZXJ0IFN5c3RlbWAgPT0gIn4iIH4gTkFfY2hhcmFjdGVyXywgICMgUmVwbGFjZSAifiIgd2l0aCBOQQ0KICAgICAgVFJVRSB+IHN0cl9yZXBsYWNlX2FsbChgRXhwZXJ0IFN5c3RlbWAsICIhIiwgIiIpICAjIFJlbW92ZSAiISINCiAgICApDQogICkgJT4lDQogICMgU2VwYXJhdGUgdGhlIHZhbHVlcyBpbiAnRXhwZXJ0IFN5c3RlbScgaW50byBtdWx0aXBsZSBjb2x1bW5zDQogIHNlcGFyYXRlKA0KICAgIGBFeHBlcnQgU3lzdGVtYCwNCiAgICBpbnRvID0gYygiRVVOSVNhIiwgIkVVTklTYiIsICJFVU5JU2MiLCAiRVVOSVNkIiksDQogICAgc2VwID0gIiwiLA0KICAgIGV4dHJhID0gImRyb3AiLCAgIyBEcm9wIGV4dHJhIHZhbHVlcyBpZiB0aGVyZSBhcmUgbW9yZSB0aGFuIGNvbHVtbnMNCiAgICBmaWxsID0gInJpZ2h0IiwgICAjIEZpbGwgbWlzc2luZyB2YWx1ZXMgd2l0aCBOQSBmb3IgY2FzZXMgd2l0aCBmZXdlciB2YWx1ZXMNCiAgICByZW1vdmUgPSBGQUxTRSAgICAjIEtlZXAgdGhlIG9yaWdpbmFsICdFeHBlcnQgU3lzdGVtJyBjb2x1bW4NCiAgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBob3cgbWFueSBkaWZmZXJlbnQgRVVOSVMgY29kZXMgaGF2ZSBiZWVuIGFzc2lnbmVkOg0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2Ygbm9uLU5BIHZhbHVlcyBhY3Jvc3MgdGhlIEVVTklTIGNvbHVtbnMNCiAgICBuX0VVTklTID0gcm93U3VtcyghaXMubmEoc2VsZWN0KC4sIHN0YXJ0c193aXRoKCJFVU5JUyIpKSkpDQogICkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhuX0VVTklTKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBFVkEgKHdpdGhvdXQgUmVTdXJ2ZXkpIG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJOdW1iZXIgb2YgZGlmZmVybnQgRVVOSVMgY29kZXMgYXNzaWduZWQiKSArIGNvb3JkX2ZsaXAoKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKG5fRVVOSVMgPiAwKSwgYWVzKG5fRVVOSVMpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIEVWQSAod2l0aG91dCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIk51bWJlciBvZiBkaWZmZXJudCBFVU5JUyBjb2RlcyBhc3NpZ25lZCIpICsgY29vcmRfZmxpcCgpDQpgYGANCg0KQ29ycmVjdCBzb21lIEVVTklTIGNvZGVzIHRoYXQgYXJlIHByb2JhYmx5IHdyb25nIChjb3BpZWQgZnJvbSBSZVN1cnZleSBub3RlYm9vayk6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKGFjcm9zcyhzdGFydHNfd2l0aCgiRVVOSVMiKSwgfiBjYXNlX3doZW4oDQogICAgLiA9PSAiTjE2TSIgfiAiTjE2IiwNCiAgICAuID09ICJTYSIgfiAiVjQiLA0KICAgIC4gPT0gIlNiIiB+ICJWNSIsDQogICAgLiA9PSAiVDFDVCIgfiAiVDFDIiwNCiAgICAuID09ICJOMTVBIiB+ICJOMTUiLA0KICAgIFRSVUUgfiAuDQogICkpKQ0KYGBgDQoNCkFkZCBjb2x1bW5zIGZvciB0aGUgZGlmZmVyZW50IEVVTklTIGxldmVscw0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICAjIEVVTklTYSBsZXZlbHMNCiAgICBFVU5JU2FfMSA9IHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDIsIDEpKSwNCiAgICBFVU5JU2FfMiA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYSkgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMiksIA0KICAgICAgc3Vic3RyKEVVTklTYSwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMikpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNhXzMgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICAgICksDQogICAgRVVOSVNhXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNiIGxldmVscw0KICAgIEVVTklTYl8xID0gc3Vic3RyKEVVTklTYiwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTYl8yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNiKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2IsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNiLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2IsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2JfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYikgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTYiwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNiXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2IpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYiwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2IsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYiwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNjIGxldmVscw0KICAgIEVVTklTY18xID0gc3Vic3RyKEVVTklTYywgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTY18yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNjKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2MsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNjLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2MsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2NfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYykgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTYywgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNjXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2MpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYywgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2MsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYywgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNkIGxldmVscw0KICAgIEVVTklTZF8xID0gc3Vic3RyKEVVTklTZCwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTZF8yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNkKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2QsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNkLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2QsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2RfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTZCkgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNkLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTZCwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNkLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNkXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2QpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTZCwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2QsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTZCwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkNCmBgYA0KDQpBZGQgZGVzY3JpcHRpb25zIGZvciBsZXZlbCAxDQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKA0KICAgIEVVTklTYV8xX2Rlc2NyID0gY2FzZV93aGVuKA0KICAgICAgRVVOSVNhXzEgPT0gIlYiIH4gIlZlZ2V0YXRlZCBtYW4tbWFkZSBoYWJpdGF0cyIsDQogICAgICBFVU5JU2FfMSA9PSAiVSIgfiAiSW5sYW5kIGhhYml0YXRzIHdpdGggbm8gb3IgbGl0dGxlIHNvaWwiLA0KICAgICAgRVVOSVNhXzEgPT0gIlQiIH4gIkZvcmVzdHMgYW5kIG90aGVyIHdvb2RlZCBsYW5kIiwNCiAgICAgIEVVTklTYV8xID09ICJTIiB+ICJIZWF0aGxhbmRzLCBzY3J1YiBhbmQgdHVuZHJhIiwNCiAgICAgIEVVTklTYV8xID09ICJSIiB+ICJHcmFzc2xhbmRzIiwNCiAgICAgIEVVTklTYV8xID09ICJRIiB+ICJXZXRsYW5kcyIsDQogICAgICBFVU5JU2FfMSA9PSAiUCIgfiAiSW5sYW5kIHdhdGVycyIsDQogICAgICBFVU5JU2FfMSA9PSAiTiIgfiAiQ29hc3RhbCBoYWJpdGF0cyIsDQogICAgICBFVU5JU2FfMSA9PSAiTUEiIH4gIk1hcmluZSBoYWJpdGF0cyIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNiXzFfZGVzY3IgPSBjYXNlX3doZW4oDQogICAgICBFVU5JU2JfMSA9PSAiViIgfiAiVmVnZXRhdGVkIG1hbi1tYWRlIGhhYml0YXRzIiwNCiAgICAgIEVVTklTYl8xID09ICJVIiB+ICJJbmxhbmQgaGFiaXRhdHMgd2l0aCBubyBvciBsaXR0bGUgc29pbCIsDQogICAgICBFVU5JU2JfMSA9PSAiVCIgfiAiRm9yZXN0cyBhbmQgb3RoZXIgd29vZGVkIGxhbmQiLA0KICAgICAgRVVOSVNiXzEgPT0gIlMiIH4gIkhlYXRobGFuZHMsIHNjcnViIGFuZCB0dW5kcmEiLA0KICAgICAgRVVOSVNiXzEgPT0gIlIiIH4gIkdyYXNzbGFuZHMiLA0KICAgICAgRVVOSVNiXzEgPT0gIlEiIH4gIldldGxhbmRzIiwNCiAgICAgIEVVTklTYl8xID09ICJQIiB+ICJJbmxhbmQgd2F0ZXJzIiwNCiAgICAgIEVVTklTYl8xID09ICJOIiB+ICJDb2FzdGFsIGhhYml0YXRzIiwNCiAgICAgIEVVTklTYl8xID09ICJNQSIgfiAiTWFyaW5lIGhhYml0YXRzIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2NfMV9kZXNjciA9IGNhc2Vfd2hlbigNCiAgICAgIEVVTklTY18xID09ICJWIiB+ICJWZWdldGF0ZWQgbWFuLW1hZGUgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNjXzEgPT0gIlUiIH4gIklubGFuZCBoYWJpdGF0cyB3aXRoIG5vIG9yIGxpdHRsZSBzb2lsIiwNCiAgICAgIEVVTklTY18xID09ICJUIiB+ICJGb3Jlc3RzIGFuZCBvdGhlciB3b29kZWQgbGFuZCIsDQogICAgICBFVU5JU2NfMSA9PSAiUyIgfiAiSGVhdGhsYW5kcywgc2NydWIgYW5kIHR1bmRyYSIsDQogICAgICBFVU5JU2NfMSA9PSAiUiIgfiAiR3Jhc3NsYW5kcyIsDQogICAgICBFVU5JU2NfMSA9PSAiUSIgfiAiV2V0bGFuZHMiLA0KICAgICAgRVVOSVNjXzEgPT0gIlAiIH4gIklubGFuZCB3YXRlcnMiLA0KICAgICAgRVVOSVNjXzEgPT0gIk4iIH4gIkNvYXN0YWwgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNjXzEgPT0gIk1BIiB+ICJNYXJpbmUgaGFiaXRhdHMiLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIEVVTklTZF8xX2Rlc2NyID0gY2FzZV93aGVuKA0KICAgICAgRVVOSVNkXzEgPT0gIlYiIH4gIlZlZ2V0YXRlZCBtYW4tbWFkZSBoYWJpdGF0cyIsDQogICAgICBFVU5JU2RfMSA9PSAiVSIgfiAiSW5sYW5kIGhhYml0YXRzIHdpdGggbm8gb3IgbGl0dGxlIHNvaWwiLA0KICAgICAgRVVOSVNkXzEgPT0gIlQiIH4gIkZvcmVzdHMgYW5kIG90aGVyIHdvb2RlZCBsYW5kIiwNCiAgICAgIEVVTklTZF8xID09ICJTIiB+ICJIZWF0aGxhbmRzLCBzY3J1YiBhbmQgdHVuZHJhIiwNCiAgICAgIEVVTklTZF8xID09ICJSIiB+ICJHcmFzc2xhbmRzIiwNCiAgICAgIEVVTklTZF8xID09ICJRIiB+ICJXZXRsYW5kcyIsDQogICAgICBFVU5JU2RfMSA9PSAiUCIgfiAiSW5sYW5kIHdhdGVycyIsDQogICAgICBFVU5JU2RfMSA9PSAiTiIgfiAiQ29hc3RhbCBoYWJpdGF0cyIsDQogICAgICBFVU5JU2RfMSA9PSAiTUEiIH4gIk1hcmluZSBoYWJpdGF0cyIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICkNCiAgKQ0KYGBgDQoNClBsb3QgZm9yIEVVTklTYV8xICh0aGUgZmlyc3QgYXNzaWduZWQgRVVOSVMgaW4gY2FzZXMgb2YgbXVsdGlwbGUgYXNzaWduYXRpb25zLCBsZXZlbCAxKToNCg0KYGBge3J9DQpnZ3Bsb3QoZGJfRVZBLCBhZXMoRVVOSVNhXzFfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIEVWQSAod2l0aG91dCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDEiKSArIGNvb3JkX2ZsaXAoKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKCFpcy5uYShFVU5JU2FfMV9kZXNjcikpLCBhZXMoRVVOSVNhXzFfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIEVWQSAod2l0aG91dCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDEiKSArIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCkNoZWNrIGlmIHZhbHVlcyBvZiBFVU5JU2FfMSwgRVVOSVNiXzEsIEVVTklTY18xIGFuZCBFVU5JU2RfMSBhcmUgYW1vbmcgdGhlIGFsbG93ZWQgdmFsdWVzOg0KDQpgYGB7cn0NCkVVTklTMV9jb2RlcyA8LSBjKCJWIiwgIlUiLCAiVCIsICJTIiwgIlIiLCAiUSIsICJQIiwgIk4iLCAiTUEiKQ0KYGBgDQoNCmBgYHtyfQ0KZGJfRVZBICU+JQ0KICBzZWxlY3QoYWxsX29mKGMoIkVVTklTYV8xIiwgIkVVTklTYl8xIiwgIkVVTklTY18xIiwgIkVVTklTZF8xIikpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiY29sdW1uIiwgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lDQogIGZpbHRlcighaXMubmEodmFsdWUpKSAlPiUNCiAgZGlzdGluY3QoY29sdW1uLCB2YWx1ZSkgJT4lDQogIG11dGF0ZShpc192YWxpZCA9IHZhbHVlICVpbiUgRVVOSVMxX2NvZGVzKSAlPiUgIA0KICBncm91cF9ieShjb2x1bW4pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgYWxsX3ZhbGlkID0gYWxsKGlzX3ZhbGlkKSwNCiAgICBpbnZhbGlkX3ZhbHVlcyA9IGxpc3QodmFsdWVbIWlzX3ZhbGlkXSkpDQpgYGANCg0KQWxsIGxldmVsIDEgdmFsdWVzIGFyZSB2YWxpZC4NCg0KIyBDSEVDSyBJRiBUSEVSRSBBUkUgV1JPTkcgRVVOSVMgVkFMVUVTIEZST00gTEVWRUwgMiBBTkQgVVANCg0KIyBJU1NVRSA5OiBNYW5pcHVsYXRlZCBwbG90cyBhbmQgaW5mbyBvbiBtYW5pcHVsYXRpb24gdHlwZQ0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgTWFuaXB1bGF0ZSAoeS9uKWApKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJNYW5pcHVsYXRpb24iKQ0KYGBgDQoNCkxpc3Qgb2YgVHlwZSBvZiBNYW5pcHVsYXRpb24gKG1peGVkIGluZm9ybWF0aW9uKSwgc3VwcG9zZWRseSBubyBtYW5pcHVsYXRlZCBwbG90cyBoZXJlICghPyk6DQoNCmBgYHtyfQ0KdW5pcXVlKGRiX0VWQSRgVHlwZSBvZiBtYW5pcHVsYXRpb25gKQ0KYGBgDQoNCiMgSVNTVUUgMTA6IExvY2F0aW9uIG1ldGhvZA0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgTG9jYXRpb24gbWV0aG9kYCkpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgRVZBICh3aXRob3V0IFJlU3VydmV5KSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiTG9jYXRpb24gbWV0aG9kIikgKyBjb29yZF9mbGlwKCkNCmBgYA0KDQojIElTU1VFIDExOiBSZXN1cnZleSBwcm9qZWN0IHR5cGVzDQoNCmBgYHtyfQ0KdW5pcXVlKGRiX0VWQSRSU19QUk9KVFlQKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSwgYWVzKFJTX1BST0pUWVAsIGZpbGw9YE1hbmlwdWxhdGUgKHkvbilgKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBFVkEgKHdpdGhvdXQgUmVTdXJ2ZXkpIG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJSZXN1cnZleSBwcm9qZWN0IHR5cGUiKSArIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KYGBgDQoNCiMgSVNTVUUgMTM6IExvY2F0aW9uIHVuY2VydGFpbnR5DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgIyBSZWRlZmluZSBwcmVjaXNpb25fbmV3LCB3aGljaCB3YXMgd3JvbmcNCiAgbXV0YXRlKHByZWNpc2lvbl9uZXcgPSBmYWN0b3IoaWZlbHNlKGlzLm5hKExvbl9wcmVjKSAmIGlzLm5hKExhdF9wcmVjKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDAsIDEpKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCwgZmlsbCA9IHByZWNpc2lvbl9uZXcpKSArDQogIGdlb21faGlzdG9ncmFtKCBjb2xvciA9ICJibGFjayIpICsNCiAgeGxhYigiTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pIikNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCA8PSA1MDApLA0KICAgICAgIGFlcyhgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCwgZmlsbCA9IHByZWNpc2lvbl9uZXcpKSArDQogIGdlb21faGlzdG9ncmFtKGNvbG9yID0gImJsYWNrIikgKw0KICB4bGFiKCJMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkgPD0gNTAwIikNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCA+IDUwMCksDQogICAgICAgYWVzKGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgLCBmaWxsID0gcHJlY2lzaW9uX25ldykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAiYmxhY2siKSArDQogIHhsYWIoIkxvY2F0aW9uIHVuY2VydGFpbnR5IChtKSA+IDUwMCIpDQpgYGANCg0KDQojIE5PIElTU1VFUyBGUk9NIEhFUkUNCg0KIyBBbHRpdHVkZSBhbmQgc2xvcGUgdmFsdWVzOiBSRVZJU0UgSUYgVVNFIQ0KDQpVbmlxdWUgc2xvcGUgdmFsdWVzOg0KDQpgYGB7cn0NCnVuaXF1ZSgoZGJfRVZBKSRgU2xvcGUgKMKwKWApICU+JSBzdHJfc29ydCgpDQpgYGANCg0KU2V0IGFsdGl0dWRlLCBzbG9wZSBhbmQgYXNwZWN0IGFzIG51bWVyaWM6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKA0KICAgICMgU29tZSBhbHRpdHVkZSB2YWx1ZXMgaGF2ZSBhICItIiBhZnRlciB0aGUgbnVtYmVyLA0KICAgICMgY29udmVydCB0byBudW1lcmljIGFmdGVyIHJlbW92aW5nIHRoYXQNCiAgICBBbHRpdHVkZSA9IGFzLm51bWVyaWMoZ3N1YigiLSIsICIiLCBBbHRpdHVkZSkpLA0KICAgICMgU29tZSBzbG9wZSB2YWx1ZXMgYXJlIG5vdGVkIGFzICJfIiBvciAiLSIsIHRoZXNlIHNob3VsZCBiZSBOQSwNCiAgICAjIG90aGVyd2lzZSBjb252ZXJ0IHRvIG51bWVyaWMNCiAgICBgU2xvcGUgKMKwKWAgPSBpZmVsc2UoYFNsb3BlICjCsClgID09ICJfIiB8IGBTbG9wZSAowrApYCA9PSAiLSIsDQogICAgICAgICAgICAgICAgICAgTkEsIGFzLm51bWVyaWMoYFNsb3BlICjCsClgKSksDQogICAgIyBDb252ZXJ0IGFzcGVjdCB2YWx1ZXMgdG8gbnVtZXJpYw0KICAgIGBBc3BlY3QgKMKwKWAgPSBhcy5udW1lcmljKGBBc3BlY3QgKMKwKWApDQogICAgKQ0KYGBgDQoNCkhpc3RvZ3JhbXM6DQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSwgYWVzKEFsdGl0dWRlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKQ0KZ2dwbG90KGRiX0VWQSwgYWVzKGBBc3BlY3QgKMKwKWApKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpDQpnZ3Bsb3QoZGJfRVZBLCBhZXMoYFNsb3BlICjCsClgKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKQ0KYGBgDQoNCmBgYHtyfQ0KcmFuZ2UoZGJfRVZBJGBTbG9wZSAowrApYCwgbmEucm09VCkNCmBgYA0KDQojIEFkZCBjb2x1bW5zIGRhdGUgYW5kIHllYXINCg0KYGBge3J9DQpkYl9FVkEgPC0gZGJfRVZBICU+JQ0KICBtdXRhdGUoZGF0ZSA9IGRteShgRGF0ZSBvZiByZWNvcmRpbmdgKSwgeWVhciA9IHllYXIoZGF0ZSkpDQpgYGANCg0KSGlzdG9ncmFtczoNCg0KYGBge3J9DQpnZ3Bsb3QoZGJfRVZBLCBhZXMoeWVhcikpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikNCmBgYA0KDQojIFBsb3Qgc2l6ZQ0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgUmVsZXbDqSBhcmVhIChtwrIpYCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikNCmBgYA0KDQpPYnNlcnZhdGlvbnMgd2l0aCBubyBpbmZvIG9uIHBsb3Qgc2l6ZToNCg0KYGBge3J9DQpucm93KGRiX0VWQSAlPiUgZmlsdGVyKGlzLm5hKGBSZWxldsOpIGFyZWEgKG3CsilgKSkpDQpgYGANCg0KIyBDb3ZlciB2YWx1ZXMgKHRvdGFsLCB0cmVlcywgc2hydWJzLCBoZXJicywgbW9zc2VzcykNCg0KYGBge3J9DQpkYl9FVkEgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhgQ292ZXIgdG90YWwgKCUpYCwgYENvdmVyIHRyZWUgbGF5ZXIgKCUpYCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGBDb3ZlciBzaHJ1YiBsYXllciAoJSlgLCBgQ292ZXIgaGVyYiBsYXllciAoJSlgLA0KICAgICAgICAgICAgICAgICAgICAgICAgYENvdmVyIG1vc3MgbGF5ZXIgKCUpYCksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIiwgYmlucyA9IDEwKSArDQogIGZhY2V0X3dyYXAofiB2YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArDQogIGxhYnMoeCA9ICJWYWx1ZSIsIHkgPSAiRnJlcXVlbmN5IikNCmBgYA0KDQpgYGB7cn0NCmRiX0VWQSAlPiUNCiAgcmVmcmFtZShhY3Jvc3MoYyhgQ292ZXIgdG90YWwgKCUpYCwgYENvdmVyIHRyZWUgbGF5ZXIgKCUpYCwNCiAgICAgICAgICAgICAgICAgICAgIGBDb3ZlciBzaHJ1YiBsYXllciAoJSlgLCBgQ292ZXIgaGVyYiBsYXllciAoJSlgLA0KICAgICAgICAgICAgICAgICAgICAgYENvdmVyIG1vc3MgbGF5ZXIgKCUpYCksIH5yYW5nZSguLCBuYS5ybSA9IFRSVUUpKSkNCmBgYA0KDQpBbGwgdmFsdWVzIE9LLg0KDQojIE1vc3NlcyBhbmQgbGljaGVucyBpZGVudGlmaWVkDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSwgYWVzKGBNb3NzZXMgaWRlbnRpZmllZCAoWS9OKWApKSArIGdlb21fYmFyKCkNCmdncGxvdChkYl9FVkEsIGFlcyhgTGljaGVucyBpZGVudGlmaWVkIChZL04pYCkpICsgZ2VvbV9iYXIoKQ0KYGBgDQoNCk5BIGluIG1vc3QgY2FzZXMuDQoNCiMgSW5mbyBvbiBIYWJpdGF0SUQgZnJvbSBESyANCg0KQmFzZWQgb24gaW5mb3JtYXRpb24gZ290IGZyb20gSmVzcGVyLg0KDQojIyBSZWFkIHRoZSBkYXRhIHNlbnQgYnkgSmVzcGVyIGZyb20gREsNCg0KYGBge3J9DQpkYl9ES19KPC1yZWFkX3RzdihoZXJlKCJkYXRhIiwgInJhdyIsDQogICAgICAgICAgICAgICAgICAgICAgICJES19OYXR1cmRhdGFfUmVzX2hhYml0YXRfaGFiX2NvZGVzX0plc3BlciIsDQogICAgICAgICAgICAgICAgICAiREtfTmF0dXJkYXRhX1Jlc19oYWJpdGF0X2hhYl9jb2Rlcy50eHQiKSkNCmBgYA0KDQojIyBBZGQgaW5mbyBvbiBIYWJpdGF0SUQgdG8gZGJfRVZBDQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgIyBLZWVwaW5nIGFsbCBvYnMgaW4gZGJfRVZBIGJ1dCBub3QgYWxsIGluIGRiX0RLX0oNCiAgbGVmdF9qb2luKGRiX0RLX0ogJT4lIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgSGFiaXRhdElEKSkNCmBgYA0KDQojIyBMaXN0IG9mIEhhYml0YXRJRA0KDQpgYGB7cn0NCnByaW50KGRiX0VWQSAlPiUgZGlzdGluY3QoSGFiaXRhdElEKSwgbiA9IDEwMCkNCmBgYA0KDQpXcml0ZSBjc3Y6DQoNCmBgYHtyfQ0Kd3JpdGVfY3N2KGRiX0VWQSAlPiUgZGlzdGluY3QoSGFiaXRhdElEKSwNCiAgICAgICAgICBoZXJlKCJkYXRhIiwgImNsZWFuIiwibGlzdF9IYWJpdGF0SURfREtfRVZBLmNzdiIpKQ0KYGBgDQoNCiMjIENhc2VzIHdpdGhvdXQgSGFiaXRhdElEIGluZm8NCg0KQ2FzZXMgd2l0aG91dCBFU3kgRVVOSVMgaGFiaXRhdDoNCg0KYGBge3J9DQpucm93KGRiX0VWQSAlPiUgZmlsdGVyKGlzLm5hKGBFeHBlcnQgU3lzdGVtYCkpKS9ucm93KGRiX0VWQSkNCmBgYA0KDQpDYXNlcyB3aXRob3V0IEVTeSBFVU5JUyBoYWJpdGF0IGJ1dCB3aXRoIEhhYml0YXRJRCBmcm9tIERLOg0KDQpgYGB7cn0NCm5yb3coZGJfRVZBICU+JSBmaWx0ZXIoaXMubmEoYEV4cGVydCBTeXN0ZW1gKSYhaXMubmEoSGFiaXRhdElEKSkpL25yb3coZGJfRVZBKQ0KYGBgDQoNCkNhc2VzIHdpdGhvdXQgRVN5IEVVTklTIGhhYml0YXQgYW5kIHdpdGhvdXQgSEFCSVRBVCBmcm9tIERLOg0KDQpgYGB7cn0NCm5yb3coZGJfRVZBICU+JSANCiAgICAgICBmaWx0ZXIoaXMubmEoYEV4cGVydCBTeXN0ZW1gKSZpcy5uYShIYWJpdGF0SUQpKSkvbnJvdyhkYl9FVkEpDQpgYGANCg0KQ2FzZXMgd2l0aG91dCBFU3kgRVVOSVMgaGFiaXRhdCBhbmQgd2l0aG91dCBIYWJpdGF0SUQgZnJvbSBESyB3aGVyZSBkYXRhIGlzIHByZXNlbmNlIC8gYWJzZW5jZToNCg0KYGBge3J9DQpucm93KGRiX0VWQSAlPiUNCiAgICAgICBmaWx0ZXIoaXMubmEoYEV4cGVydCBTeXN0ZW1gKSAmDQogICAgICAgICAgICAgICAgaXMubmEoSGFiaXRhdElEKSAmDQogICAgICAgICAgICAgICAgYENvdmVyIGFidW5kYW5jZSBzY2FsZWAgPT0gIlByZXNlbmNlL0Fic2VuY2UiKSkvDQogIG5yb3coZGJfRVZBKQ0KYGBgDQoNCkNhc2VzIHdpdGhvdXQgRVN5IEVVTklTIGhhYml0YXQgYW5kIHdpdGhvdXQgSGFiaXRhdElEIGZyb20gREsgd2hlcmUgZGF0YSBpcyBub3QgcHJlc2VuY2UgLyBhYnNlbmNlOg0KDQpgYGB7cn0NCm5yb3coZGJfRVZBICU+JQ0KICAgICAgIGZpbHRlcihpcy5uYShgRXhwZXJ0IFN5c3RlbWApICYNCiAgICAgICAgICAgICAgICBpcy5uYShIYWJpdGF0SUQpICYNCiAgICAgICAgICAgICAgICBgQ292ZXIgYWJ1bmRhbmNlIHNjYWxlYCAhPSAiUHJlc2VuY2UvQWJzZW5jZSIpKS8NCiAgbnJvdyhkYl9FVkEpDQpgYGANCg0KIyMgQ2hhbmdlIHNvbWUgQW5uZXggSSBoYWJpdGF0IGNvZGVzIHRoYXQgd2VyZSB3cm9uZw0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZShIYWJpdGF0SUQgPSBhcy5jaGFyYWN0ZXIoSGFiaXRhdElEKSkgJT4lDQogIG11dGF0ZShIYWJpdGF0SUQgPSBpZmVsc2UoSGFiaXRhdElEID09ICI5OTk4IiwgIjkxRDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShIYWJpdGF0SUQgPT0gIjk5OTkiLCAiOTFFMCIsIEhhYml0YXRJRCkpKQ0KYGBgDQoNCiMgQWRkIGluZm8gb24gY29ycmVzcG9uZGVuY2VzIEhhYml0YXRJRCAoREssIEplc3BlcikgLSBFVU5JUw0KDQpSZWFkIGNvcnJlc3BvbmRlbmNlcyBmaWxlOg0KDQpgYGB7cn0NCmNvcnJlc3BvbmRlbmNlczwtcmVhZF9leGNlbChoZXJlKCJkYXRhIiwgImVkaXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29ycmVzcG9uZGVuY2VfSGFiaXRhdElEX0RLLnhsc3giKSkNCmBgYA0KDQpBZGQgaW5mbyB0byBkYl9FVkE6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgIyBLZWVwaW5nIGFsbCBvYnMgaW4gZGJfRVZBIGJ1dCBub3QgYWxsIGluIGRiX0RLX0oNCiAgbGVmdF9qb2luKGNvcnJlc3BvbmRlbmNlcyAlPiUgc2VsZWN0KEhhYml0YXRJRCwgRVVOSVMpKQ0KYGBgDQoNCkNvcnJlY3QgTkEgdmFsdWVzIGluIEVVTklTDQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKEVVTklTID0gaWZlbHNlKEVVTklTID09ICJOQSIsIE5BLCBFVU5JUykpDQpgYGANCg0KQWRkIGluZm8gb24gRVVOSVMgKERLKSB0byBFVU5JU2E6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKEVVTklTYSA9DQogICAgICAgICAgICMgSWYgRVVOSVMgKERLKSBpcyBhdmFpbGFibGUsIGFkZCBhcyBFVU5JU2ENCiAgICAgICAgICAgaWZlbHNlKCFpcy5uYShFVU5JUyksIEVVTklTLCANCiAgICAgICAgICAgICAgICAgICMgT3RoZXJ3aXNlIGtlZXAgRVVOSVNhDQogICAgICAgICAgICAgICAgICBFVU5JU2EpLA0KICAgICAgICAgRVVOSVNfYXNzaWduYXRpb24gPSBpZmVsc2UoIWlzLm5hKEVVTklTKSwgIkluZm8gZnJvbSBESyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoRVVOSVNhKSwgIk5vdCBwb3NzaWJsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV4cGVydCBzeXN0ZW0iKSkpICU+JQ0KICAjIFJlbW92ZSBjb2x1bW4gRVVOSVMgKERLKQ0KICBzZWxlY3QoLUVVTklTKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSwgYWVzKEVVTklTX2Fzc2lnbmF0aW9uKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBFVkEgKG5vdCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGFzc2lnbmF0aW9uIikNCiMgVmVyeSBzbWFsbCAlIHdpdGggSW5mbyBmcm9tIERLLCBpbnZpc2libGUgaW4gdGhlIGdyYXBoDQpgYGANCg0KIyMgVXBkYXRlIGNvbHVtbnMgZm9yIEVVTklTIGxldmVscyBhbmQgZGVzY3JpcHRpb25zDQoNClVwZGF0ZSB0aGUgY29sdW1ucyBmb3IgdGhlIGRpZmZlcmVudCBFVU5JU3MgbGV2ZWxzOg0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICAjIEVVTklTYSBsZXZlbHMNCiAgICBFVU5JU2FfMSA9IHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDIsIDEpKSwNCiAgICBFVU5JU2FfMiA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYSkgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMiksIA0KICAgICAgc3Vic3RyKEVVTklTYSwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMikpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNhXzMgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICAgICksDQogICAgRVVOSVNhXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkgJT4lDQogICMgUmVtb3ZlIEhhYml0YXRJRCBjb2x1bW4NCiAgc2VsZWN0KC1IYWJpdGF0SUQpDQpgYGANCg0KVXBkYXRlIGNvbHVtbnMgd2l0aCBkZXNjcmlwdGlvbnMgZm9yIHRoZSBsZXZlbCAxIGNvZGVzOg0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICBFVU5JU2FfMV9kZXNjciA9IGNhc2Vfd2hlbigNCiAgICAgIEVVTklTYV8xID09ICJWIiB+ICJWZWdldGF0ZWQgbWFuLW1hZGUgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNhXzEgPT0gIlUiIH4gIklubGFuZCBoYWJpdGF0cyB3aXRoIG5vIG9yIGxpdHRsZSBzb2lsIiwNCiAgICAgIEVVTklTYV8xID09ICJUIiB+ICJGb3Jlc3RzIGFuZCBvdGhlciB3b29kZWQgbGFuZCIsDQogICAgICBFVU5JU2FfMSA9PSAiUyIgfiAiSGVhdGhsYW5kcywgc2NydWIgYW5kIHR1bmRyYSIsDQogICAgICBFVU5JU2FfMSA9PSAiUiIgfiAiR3Jhc3NsYW5kcyIsDQogICAgICBFVU5JU2FfMSA9PSAiUSIgfiAiV2V0bGFuZHMiLA0KICAgICAgRVVOSVNhXzEgPT0gIlAiIH4gIklubGFuZCB3YXRlcnMiLA0KICAgICAgRVVOSVNhXzEgPT0gIk4iIH4gIkNvYXN0YWwgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNhXzEgPT0gIk1BIiB+ICJNYXJpbmUgaGFiaXRhdHMiLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkNCmBgYA0KDQojIyBOdW1iZXIgb2YgZGlmZmVyZW50IEVVTklTIGNvZGVzDQoNClJlY2FsY3VsYXRlIGhvdyBtYW55IGRpZmZlcmVudCBFVU5JUyBjb2RlcyBoYXZlIGJlZW4gYXNzaWduZWQ6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBub24tTkEgdmFsdWVzIGFjcm9zcyB0aGUgRVVOSVMgY29sdW1ucw0KICAgIG5fRVVOSVMgPSByb3dTdW1zKCFpcy5uYShzZWxlY3QoLiwgRVVOSVNhOkVVTklTZCkpKQ0KICApDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGJfRVZBLCBhZXMobl9FVU5JUykpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgRVZBIChub3QgUmVTdXJ2ZXkpIG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJOdW1iZXIgb2YgZGlmZmVybnQgRVVOSVMgY29kZXMgYXNzaWduZWQiKSArIGNvb3JkX2ZsaXAoKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKG5fRVVOSVMgPiAwKSwgYWVzKG5fRVVOSVMpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIEVWQSAobm90IFJlU3VydmV5KSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiTnVtYmVyIG9mIGRpZmZlcm50IEVVTklTIGNvZGVzIGFzc2lnbmVkIikgKyBjb29yZF9mbGlwKCkNCmBgYA0KDQpOZXcgcGxvdCBmb3IgRVVOSVNhXzEgKHRoZSBmaXJzdCBhc3NpZ25lZCBFVU5JUyBpbiBjYXNlcyBvZiBtdWx0aXBsZSBhc3NpZ25hdGlvbnMsIGxldmVsIDEpOg0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhFVU5JU2FfMV9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgRVZBIChub3QgUmVTdXJ2ZXkpIG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAxIikgKyBjb29yZF9mbGlwKCkNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcighaXMubmEoRVVOSVNhXzFfZGVzY3IpKSwgYWVzKEVVTklTYV8xX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBFVkEgKG5vdCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDEiKSArIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCiMgQWRkIGluZm8gb24gZGVzY3JpcHRpb25zIGZvciBFVU5JUyBsZXZlbHMgMi00IC0gQ0hFQ0sgSUYgVVNFIQ0KDQpgYGB7cn0NCmRlc2NyaXB0aW9uczwtcmVhZF9leGNlbChoZXJlKCJkYXRhIiwgImVkaXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRVVOSVMtSGFiaXRhdHMtMjAyMS0wNi0wMV9tb2RpZmllZC54bHN4IikpDQpgYGANCg0KYGBge3J9DQojIERlZmluZSB0aGUgY29sdW1ucyBhbmQgY29ycmVzcG9uZGluZyBkZXNjcmlwdGlvbiBjb2x1bW4gbmFtZXMNCmV1bmlzX2NvbHMgPC0gYygiRVVOSVNhXzIiLCAiRVVOSVNhXzMiLCAiRVVOSVNhXzQiLA0KICAgICAgICAgICAgICAgICJFVU5JU2JfMiIsICJFVU5JU2JfMyIsICJFVU5JU2JfNCIsIA0KICAgICAgICAgICAgICAgICJFVU5JU2NfMiIsICJFVU5JU2NfMyIsICJFVU5JU2NfNCIsDQogICAgICAgICAgICAgICAgIkVVTklTZF8yIiwgIkVVTklTZF8zIiwgIkVVTklTZF80IikNCg0KIyBDcmVhdGUgY29ycmVzcG9uZGluZyBkZXNjcmlwdGlvbiBjb2x1bW4gbmFtZXMNCmRlc2NyX2NvbF9uYW1lcyA8LSBwYXN0ZTAoZXVuaXNfY29scywgIl9kZXNjciIpDQoNCiMgVXNlIHJlZHVjZSB0byBsb29wIHRocm91Z2ggdGhlIGNvbHVtbnMgYW5kIGpvaW4gZHluYW1pY2FsbHkgYmFzZWQgb24gbGV2ZWwNCmRiX0VWQSA8LSByZWR1Y2Uoc2VxX2Fsb25nKGV1bmlzX2NvbHMpLCBmdW5jdGlvbihkYXRhLCBpKSB7DQogICMgRXh0cmFjdCBsZXZlbCBudW1iZXIgZnJvbSB0aGUgY29sdW1uIG5hbWUgKGUuZy4sIEVVTklTYV8yIC0+IDIpDQogIGxldmVsIDwtIGFzLm51bWVyaWMoZ3N1YigiXFxEIiwgIiIsIGV1bmlzX2NvbHNbaV0pKQ0KICANCiAgIyBGaWx0ZXIgZGVzY3JpcHRpb25zIGZvciB0aGUgY29ycmVzcG9uZGluZyBsZXZlbA0KICBkZXNjcmlwdGlvbnNfbGV2ZWwgPC0gZGVzY3JpcHRpb25zICU+JQ0KICAgIGZpbHRlcihsZXZlbCA9PSBsZXZlbCkgJT4lDQogICAgc2VsZWN0KGBFVU5JUyAyMDIwIGNvZGVgLCBgRVVOSVMtMjAyMSBoYWJpdGF0IG5hbWVgKQ0KICANCiAgIyBQZXJmb3JtIHRoZSBsZWZ0X2pvaW4gYW5kIHJlbmFtZSB0aGUgY29sdW1uIGR5bmFtaWNhbGx5DQogIGRhdGEgJT4lDQogICAgbGVmdF9qb2luKA0KICAgICAgZGVzY3JpcHRpb25zX2xldmVsLA0KICAgICAgYnkgPSBzZXROYW1lcygiRVVOSVMgMjAyMCBjb2RlIiwgZXVuaXNfY29sc1tpXSkNCiAgICApICU+JQ0KICAgIHJlbmFtZSghIWRlc2NyX2NvbF9uYW1lc1tpXSA6PSBgRVVOSVMtMjAyMSBoYWJpdGF0IG5hbWVgKQ0KfSwgLmluaXQgPSBkYl9FVkEpDQpgYGANCg0KVGhlIG1hdGNoaW5nIGRpZCBub3Qgd29yayBzb21ldGltZXMsIGNvcnJlY3QhDQoNCmBgYHtyfQ0KIyBDb3JyZWN0IEVVTklTYSBsZXZlbHMgMi00IGRlc2NyaXB0aW9ucw0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKEVVTklTYV8yX2Rlc2NyID0gDQogICAgICAgICAgIGlmZWxzZSghaXMubmEoRVVOSVNhXzJfZGVzY3IpLCBFVU5JU2FfMl9kZXNjciwNCiAgICAgICAgICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBmIiB+ICJGcmVzaC13YXRlciBzdWJtZXJnZWQgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJQaiIgfiAiU3RvbmV3b3J0IHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUjQiIH4gIkFscGluZSBhbmQgc3ViYWxwaW5lIGdyYXNzbGFuZHMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUGIiIH4gIkNhbGNhcmVvdXMgc3ByaW5nIGFuZCBzcHJpbmcgYnJvb2siLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUWIiIH4gIldldGxhbmRzIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlIzIiB+ICJTZWFzb25hbGx5IHdldCBhbmQgd2V0IGdyYXNzbGFuZHMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUWEiIH4gIk1pcmVzIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBhIiB+ICJCYXNlLXBvb3Igc3ByaW5nIGFuZCBzcHJpbmcgYnJvb2siLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUGgiIH4gIk9saWdvdHJvcGhpYy13YXRlciB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBnIiB+ICJGcmVzaC13YXRlciBueW1waGFlaWQgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09DQogICAgICAgICAgICAgICAgICAgICAgIlBkIiB+ICJGcmVzaC13YXRlciBzbWFsbCBwbGV1c3RvcGh5dGUgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJQYyIgfiAiQnJhY2tpc2gtd2F0ZXIgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09DQogICAgICAgICAgICAgICAgICAgICAgIlBlIiB+ICJGcmVzaC13YXRlciBsYXJnZSBwbGV1c3RvcGh5dGUgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJQaSIgfiAiRHlzdHJvcGhpYy13YXRlciB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlMxIiB+ICJUdW5kcmEiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PQ0KICAgICAgICAgICAgICAgICAgICAgICJVNyIgfiAiVW52ZWdldGF0ZWQgb3Igc3BhcnNlbHkgdmVnZXRhdGVkIGdyYXZlbCBiYXJzIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlE2IiB+ICJQZXJpb2RpY2FsbHkgZXhwb3NlZCBzaG9yZXMiLA0KICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykNCiAgICAgICAgICAgICAgICAgICksDQogICAgICAgICBFVU5JU2FfM19kZXNjciA9IA0KICAgICAgICAgICBpZmVsc2UoIWlzLm5hKEVVTklTYV8zX2Rlc2NyKSwgRVVOSVNhXzNfZGVzY3IsDQogICAgICAgICAgICAgICAgICBjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8zID09IlU3MSIgfiAiVW52ZWdldGF0ZWQgb3Igc3BhcnNlbHkgdmVnZXRhdGVkIGdyYXZlbCBiYXIgaW4gbW9udGFuZSBhbmQgYWxwaW5lIHJlZ2lvbnMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMyA9PSJRNjEiIH4gIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlIHdpdGggc3RhYmxlLCBldXRyb3BoaWMgc2VkaW1lbnRzIHdpdGggcGlvbmVlciBvciBlcGhlbWVyYWwgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8zID09IlE2MiIgfiAiUGVyaW9kaWNhbGx5IGV4cG9zZWQgc2hvcmUgd2l0aCBzdGFibGUsIG1lc290cm9waGljIHNlZGltZW50cyB3aXRoIHBpb25lZXIgb3IgZXBoZW1lcmFsIHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICAgICAgICAgICAgICAgICApKQ0KICAgICAgICAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb3JyZWN0IEVVTklTYiBsZXZlbHMgMi00IGRlc2NyaXB0aW9ucw0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKEVVTklTYl8yX2Rlc2NyID0gDQogICAgICAgICAgIGlmZWxzZSghaXMubmEoRVVOSVNiXzJfZGVzY3IpLCBFVU5JU2JfMl9kZXNjciwNCiAgICAgICAgICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNiXzIgPT0gIlBqIiB+ICJTdG9uZXdvcnQgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYl8yID09ICJSNCIgfiAiQWxwaW5lIGFuZCBzdWJhbHBpbmUgZ3Jhc3NsYW5kcyIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYl8yID09ICJQZiIgfiAiRnJlc2gtd2F0ZXIgc3VibWVyZ2VkIHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykNCiAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICkNCmBgYA0KDQpFVU5JU2MgYW5kIEVVTklTZCBsZXZlbHMgMi00IGFyZSBPSy4NCiANCiMgTm90ZXMgRVVOSVMgY29kZXMgLSB0byBjaGFuZ2U/DQoNCmh0dHBzOi8vd3d3LnNjaS5tdW5pLmN6L2JvdGFueS9jaHl0cnkvU2NoYW1pbmVlX2V0YWwyMDIxX0VFQS1SZXBvcnQtQXF1YXRpYy1XZXRsYW5kLWhhYml0YXRzLnBkZg0KDQpFVU5JU2FfMiA9PSAiUTYiIDogIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlcyINCkVVTklTYV8zID0gIlE2MSIgOiAiUGVyaW9kaWNhbGx5IGV4cG9zZWQgc2hvcmUgd2l0aCBzdGFibGUsIGV1dHJvcGhpYyBzZWRpbWVudHMgd2l0aA0KcGlvbmVlciBvciBlcGhlbWVyYWwgdmVnZXRhdGlvbiINCkVVTklTYV8zID09ICJRNjIiIDogIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlIHdpdGggc3RhYmxlLCBtZXNvdHJvcGhpYyBzZWRpbWVudHMgd2l0aCBwaW9uZWVyIG9yIGVwaGVtZXJhbCB2ZWdldGF0aW9uIg0KDQpUaGlzIGNsYXNzaWZpY2F0aW9uIG9mIFEgKyBudW1iZXJzIGlzIG5vdyBjb2V4aXN0aW5nIGluIHRoZSBkYXRhYmFzZSB3aXRoIFFhICYgUWIgKG1ldGFkYXRhKS4gSG93IHRvIHByb2NlZWQ/DQoNCmBgYHtyfQ0KZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlEiKSAlPiUgZGlzdGluY3QoRVVOSVNhXzIpDQpgYGANCg0KDQojIFBsb3RzIG9mIGxldmVsLTIgY2F0ZWdvcmllcyB3aXRoaW4gZWFjaCBsZXZlbCAxIGNhdGVnb3J5DQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJNQSIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJNQSIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJNQV9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiTiIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJOIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIk5fbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNCxoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpnZ3Bsb3QoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlAiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZShkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUCIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJQX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTQsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJRIiksIGFlcyhFVU5JU2FfMl9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDIiKSArIGNvb3JkX2ZsaXAoKSArDQogIGdndGl0bGUoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlEiKSAlPiUgZGlzdGluY3QoRVVOSVNhXzFfZGVzY3IpKQ0KZ2dzYXZlKGZpbGVuYW1lPWhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwiUV9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUiIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJSIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIlJfbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNCxoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpnZ3Bsb3QoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlMiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZShkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUyIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJTX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTYsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJUIiksIGFlcyhFVU5JU2FfMl9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDIiKSArIGNvb3JkX2ZsaXAoKSArDQogIGdndGl0bGUoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlQiKSAlPiUgZGlzdGluY3QoRVVOSVNhXzFfZGVzY3IpKQ0KZ2dzYXZlKGZpbGVuYW1lPWhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwiVF9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiVSIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJVIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIlVfbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNixoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpnZ3Bsb3QoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlYiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZShkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiViIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJWX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTQsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KYGBgDQoNCiMgU2F2ZSB0byBjbGVhbiBkYXRhDQoNClNhdmUgc28tZmFyIGNsZWFuIGRhdGFmaWxlIGZvciBFVkEgZGF0YWJhc2UgKG5vdCByZXN1cnZleXMpOg0KDQpgYGB7cn0NCndyaXRlX3RzdihkYl9FVkEsaGVyZSgiZGF0YSIsICJjbGVhbiIsImRiX0VWQV9jbGVhbi5jc3YiKSkNCmBgYA0KDQojIFNlc3Npb24gaW5mbw0KDQpgYGB7cn0NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQoNCg0K